using System;
using Firefly.Framework.ContentManagement;
using Microsoft.Xna.Framework.Graphics;

namespace Firefly.Framework.Components.Terrain
{
    public class HeightMap
    {
        public float[,] HeightStore;
        private Texture2D _texture;
        private string _heightMap;

        public int Width { get; private set; }
        public int Height { get; private set; }
        public float this[int x, int y]
        {
            get { return HeightStore[x, y]; }
        }
        public float MinHeight { get; private set; }

        public HeightMap(string heightMap, int smothingPasses)
        {
            _heightMap = heightMap;
            _texture = ContentService.Instance.Load<Texture2D>(_heightMap);

            if (_texture == null)
            {
                throw new NullReferenceException("The heightmap texture cannot be null.");
            }

            Width = _texture.Width;
            Height = _texture.Height;
            GenerateHeightMap();
            SmoothHeightMap(smothingPasses);
            ContentService.Instance.Release(_heightMap);
        }

        private void GenerateHeightMap()
        {
            Color[] heightMapColors = new Color[Width * Height];
            _texture.GetData(heightMapColors);
            HeightStore = new float[Width, Height];
            MinHeight = heightMapColors[0].R;

            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    HeightStore[x, y] = heightMapColors[x + (y * Width)].R;

                    if (HeightStore[x, y] < MinHeight)
                    {
                        MinHeight = HeightStore[x, y];
                    }
                }
            }

            heightMapColors = null;
        }

        private void SmoothHeightMap(int numberOfPasses)
        {
            float[,] newHeightStore;

            while (numberOfPasses > 0)
            {
                numberOfPasses--;

                // Width and Height should be equal and power-of-two + 1 values.
                newHeightStore = new float[Width, Height];

                for (int x = 0; x < Width; x++)
                {
                    for (int y = 0; y < Height; y++)
                    {
                        int adjacentSections = 0;
                        float sectionsTotal = 0.0f;

                        // Check to left.
                        if ((x - 1) > 0)
                        {
                            sectionsTotal += HeightStore[x - 1, y];
                            adjacentSections++;

                            // Check up and to the left.
                            if ((y - 1) > 0)
                            {
                                sectionsTotal += HeightStore[x - 1, y - 1];
                                adjacentSections++;
                            }

                            // Check down and to the left.
                            if ((y + 1) < Height)
                            {
                                sectionsTotal += HeightStore[x - 1, y + 1];
                                adjacentSections++;
                            }
                        }

                        // Check to right.
                        if ((x + 1) < Width)
                        {
                            sectionsTotal += HeightStore[x + 1, y];
                            adjacentSections++;

                            // Check up and to the right.
                            if ((y - 1) > 0)
                            {
                                sectionsTotal += HeightStore[x + 1, y - 1];
                                adjacentSections++;
                            }

                            // Check down and to the right.
                            if ((y + 1) < Height)
                            {
                                sectionsTotal += HeightStore[x + 1, y + 1];
                                adjacentSections++;
                            }
                        }

                        // Check above.
                        if ((y - 1) > 0)
                        {
                            sectionsTotal += HeightStore[x, y - 1];
                            adjacentSections++;
                        }

                        // Check below.
                        if ((y + 1) < Height)
                        {
                            sectionsTotal += HeightStore[x, y + 1];
                            adjacentSections++;
                        }

                        newHeightStore[x, y] = (HeightStore[x, y] + (sectionsTotal / adjacentSections)) * 0.5f;
                    }
                }

                // Overwrite the HeightStore info with our new smoothed info.
                HeightStore = newHeightStore;
            }
        }
    }
}